package ldh;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import ldh.serialize.util.TransferUtil;

import org.apache.commons.codec.Charsets;

public class LdhUserUtil {

	public static byte[] toBytes(LdhUser user) {
		ByteArrayOutputStream out = new ByteArrayOutputStream(300);
		try {
			out.write(TransferUtil.intToBytes(user.getAge()));
			byte[] tt = user.getId().getBytes(Charsets.UTF_8);
			out.write(TransferUtil.intToBytes(tt.length));
			out.write(tt);
			
			out.write(TransferUtil.longToBytes(user.getCreateTime().getTime()));
			tt = user.getName().getBytes(Charsets.UTF_8);
			out.write(TransferUtil.intToBytes(tt.length));
			out.write(tt);
			
			out.write(TransferUtil.intToBytes(user.getProps().size()));
			for (Entry<String, String> entry : user.getProps().entrySet()) {
				writeString(out, entry.getKey());
				writeString(out, entry.getValue());
			}
			
			out.write(TransferUtil.intToBytes(user.getAddress().size()));
			for (String add : user.getAddress()) {
				writeString(out, add);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return out.toByteArray();
	}
	
	public static LdhUser toObject(byte[] bytes) throws IOException {
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		try {
			short age = readShort(in);
			String id = readString(in);
			long time = readLong(in);
			String name = readString(in);
			
			int size = readInteger(in);
			Map<String, String> props = new HashMap<String, String>();
			for (int i=0; i<size; i++) {
				String key = readString(in);
				String value = readString(in);
				props.put(key, value);
			}
			
			int lg = readInteger(in);
			List<String> adds = new ArrayList<String>(lg);
			for (int i=0; i<lg; i++) {
				String item = readString(in);
				adds.add(item);
			}
			
			LdhUser user = new LdhUser();
			user.setAge(age);
			user.setAddress(adds);
			user.setId(id);
			user.setName(name);
			user.setCreateTime(new Date(time));
			user.setProps(props);
			return user;
		} catch (IOException e) {
			throw e;
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static LdhUser toObject2(byte[] bytes) throws IOException {
		try {
			Position p = new Position();
			short age = readShort(bytes, p);
			String id = readString(bytes, p);
			long time = readLong(bytes, p);
			String name = readString(bytes, p);
			
			int size = readInteger(bytes, p);
			Map<String, String> props = new HashMap<String, String>();
			for (int i=0; i<size; i++) {
				String key = readString(bytes, p);
				String value = readString(bytes, p);
				props.put(key, value);
			}
			
			int lg = readInteger(bytes, p);
			List<String> adds = new ArrayList<String>(lg);
			for (int i=0; i<lg; i++) {
				String item = readString(bytes, p);
				adds.add(item);
			}
			
			LdhUser user = new LdhUser();
			user.setAge(age);
			user.setAddress(adds);
			user.setId(id);
			user.setName(name);
			user.setCreateTime(new Date(time));
			user.setProps(props);
			return user;
		} catch (IOException e) {
			throw e;
		} 
	}
	
	public static LdhUser toObject3(byte[] bytes) throws IOException {
		ByteBuffer buffer = ByteBuffer.wrap(bytes);
		try {
			short age = buffer.getShort();
			String id = readString(buffer);
			long time = buffer.getLong();
			String name = readString(buffer);
			
			int size = buffer.getInt();
			Map<String, String> props = new HashMap<String, String>();
			for (int i=0; i<size; i++) {
				String key = readString(buffer);
				String value = readString(buffer);
				props.put(key, value);
			}
			
			int lg = buffer.getInt();
			List<String> adds = new ArrayList<String>(lg);
			for (int i=0; i<lg; i++) {
				String item = readString(buffer);
				adds.add(item);
			}
			
			LdhUser user = new LdhUser();
			user.setAge(age);
			user.setAddress(adds);
			user.setId(id);
			user.setName(name);
			user.setCreateTime(new Date(time));
			user.setProps(props);
			return user;
		} catch (IOException e) {
			throw e;
		} finally {
			buffer.flip();
		}
	}
	
	private static void writeString(OutputStream out, String str) throws IOException {
		byte[] tt = str.getBytes(Charsets.UTF_8);
		out.write(TransferUtil.intToBytes(tt.length));
		out.write(tt);
	}
	
	private static String readString(InputStream in) throws IOException {
		byte[] lg = new byte[4];
		in.read(lg);
		int ll = TransferUtil.bytesToInt(lg, 0);
		byte[] bbs = new byte[ll];
		in.read(bbs);
		return new String(bbs, Charsets.UTF_8);
	}
	
	private static String readString(byte[] bytes, Position idx) throws IOException {
		int ll = TransferUtil.bytesToInt(bytes, idx.position);
		idx.increame(4);
		byte[] bbs = new byte[ll];
		System.arraycopy(bytes, idx.position, bbs, 0, bbs.length);
		idx.increame(ll);
		return new String(bbs, Charsets.UTF_8);
	}
	
	private static String readString(ByteBuffer buffer) throws IOException {
		int ll = buffer.getInt();
		byte[] bbs = new byte[ll];
		buffer.get(bbs);
		return new String(bbs, Charsets.UTF_8);
	}
	
	private static Short readShort(InputStream in) throws IOException {
		byte[] lg = new byte[2];
		in.read(lg);
		short ll = TransferUtil.bytesToShort(lg, 0);
		return ll;
	}
	
	private static Integer readInteger(InputStream in) throws IOException {
		byte[] lg = new byte[4];
		in.read(lg);
		int ll = TransferUtil.bytesToInt(lg, 0);
		return ll;
	}
	
	private static Integer readInteger(byte[] bytes, Position idx) throws IOException {
		int ll = TransferUtil.bytesToInt(bytes, idx.position);
		idx.increame(4);
		return ll;
	}
	
	private static Short readShort(byte[] bytes, Position idx) throws IOException {
		short ll = TransferUtil.bytesToShort(bytes, idx.position);
		idx.increame(4);
		return ll;
	}
	
	private static Long readLong(InputStream in) throws IOException {
		byte[] lg = new byte[8];
		in.read(lg);
		long ll = TransferUtil.bytesToLong(lg, 0);
		return ll;
	}
	
	private static Long readLong(byte[] bytes, Position p) throws IOException {
		long ll = TransferUtil.bytesToLong(bytes, p.position);
		p.increame(8);
		return ll;
	}
	
	private static class Position {
		public int position = 0;
		
		public void increame(int t) {
			this.position += t;
		}
	}
}
